﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace memcached
{
    /// <summary>
    /// Base volatile transaction manager implementation.
    /// </summary>
    /// <remarks>
    /// When a resource manager fails, all of its enlisted transactions are aborted except for those that are prepared or committed prior to the failure.
    /// The scope of a TransactionScope spans all operations on the same thread inside the TransactionScope block.
    /// Only volatile enlistment supported. If the app crashes or is killed, your transaction will be stuck half-way.
    /// </remarks>
    public abstract class BaseResourceManager :
        System.Transactions.IEnlistmentNotification, System.Transactions.ISinglePhaseNotification
    {
        /// <summary>
        /// True if any action taken.
        /// </summary>
        protected abstract bool ChangesMade { get; }

        /// <summary>
        /// 
        /// </summary>
        protected virtual void Commit()
        {
            // throw exception if failed
#if DEBUG
            System.Diagnostics.Trace.TraceInformation("Committed");
#endif
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual void Rollback()
        {
            // can be called after Commit failed with exception
#if DEBUG
            System.Diagnostics.Trace.TraceInformation("Rolled back");
#endif
        }

        /// <summary>
        /// Notifies an enlisted object that a transaction is being committed.
        /// </summary>
        /// <param name="enlistment"></param>
        public void Commit(System.Transactions.Enlistment enlistment)
        {
            Commit();
            enlistment.Done();
        }

        /// <summary>
        /// Notifies an enlisted object that the status of a transaction is in doubt.
        /// </summary>
        /// <param name="enlistment"></param>
        public void InDoubt(System.Transactions.Enlistment enlistment)
        {
            // transaction manager lost contact with one or more participant
#if DEBUG
            System.Diagnostics.Trace.TraceWarning("InDoubt");
#endif
            enlistment.Done();
        }

        /// <summary>
        /// Notifies an enlisted object that a transaction is being prepared for commitment.
        /// When the application commits the transaction, the transaction manager initiates the two-phase commit protocol. The transaction manager first asks each enlisted resource manager if it is prepared to commit the transaction. The resource manager must prepare to commit—it readies itself to either commit or abort the transaction.
        /// </summary>
        /// <param name="preparingEnlistment"></param>
        public void Prepare(System.Transactions.PreparingEnlistment preparingEnlistment)
        {
            if (ChangesMade)
                preparingEnlistment.Prepared();// vote to Commit
            else
                preparingEnlistment.Done();
            //if you call ForceRollback() within your implementation of the Prepare() method,
            //the Rollback() method will not be called on your resource.
        }

        /// <summary>
        /// Notifies an enlisted object that a transaction is being rolled back (aborted).
        /// </summary>
        /// <param name="enlistment"></param>
        public void Rollback(System.Transactions.Enlistment enlistment)
        {
            if (ChangesMade)
                try
                {
                    Rollback();
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.TraceError(e.ToString());// fail silently, not much else we can do
                }
            enlistment.Done();
        }

        /// <summary>
        /// Represents the resource manager's implementation of the callback for the
        ///     single phase commit optimization.
        /// </summary>
        /// <param name="singlePhaseEnlistment"></param>
        public void SinglePhaseCommit(System.Transactions.SinglePhaseEnlistment singlePhaseEnlistment)
        {
            if (!ChangesMade)
            {
                singlePhaseEnlistment.Done();
                return;
            }
            try
            {
                Commit();
            }
            catch (Exception e)
            {
                singlePhaseEnlistment.Aborted(e);// vote to rollback
                return;
            }
            singlePhaseEnlistment.Committed();// vote to commit
        }
    }
}